import java.awt.BorderLayout;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;


public class tablaSimbolos {
	
	ArrayList<filaTS> tabla;
	Object tablaActual;
	ArrayList<String> pilaVariablesActuales;
	Integer temporal=0;
	

	
	public tablaSimbolos() {

		tabla = new ArrayList<filaTS>();
		tablaActual = tabla;
		pilaVariablesActuales = new ArrayList<String>();
		
	}
	
	public boolean insertarVariable(String lexema, tipo tipo){ 
		boolean error=true; int posicion;
		filaTS fila=null; 
		tipoVisibilidad visibilidad = null;
		int indice;
		
		System.out.println("TS: InsertarVariable: "+lexema+" "+tipo);
		
		if (tablaActual==tabla){
			
			if (lexema.equals("ACTUAL")){
				
				if ((indice = pilaVariablesActuales.get(0).indexOf("|"))!=-1){
					
					posicion = obtenerPosicion ( pilaVariablesActuales.get(0).substring(0, indice) );
					if ( pilaVariablesActuales.get(0).length() > indice+1 )
						error = tabla.get(posicion).tablaVector.actualizarTVector( Character.getNumericValue(pilaVariablesActuales.get(0).charAt(indice+1) )  , tipo);
					else 
						System.out.println("TS: InsertarVariable: "+pilaVariablesActuales.get(0)+" no se ha podido actualizar");

					
				}else{
					posicion = obtenerPosicion(pilaVariablesActuales.get(0));
					if (posicion != -1){				
						System.out.println("TS: InsertarVariable: Si estaba en tabla Simbolos  "+ pilaVariablesActuales.get(0));
						if (tabla.get(posicion).tipo != tipo.VECTOR){
							tabla.get(posicion).tipo = tipo; System.out.println("+++++++++++++++" + tipo);
						}
						error = false;
					}else{
						fila = new filaTS(pilaVariablesActuales.get(0), tipo, tipoVisibilidad.GLOBAL, 0, 0, null, null, null);
						tabla.add(fila);
						System.out.println("TS: InsertarVariable: No estaba en tabla Simbolos: Crear filaTS: "+ pilaVariablesActuales.get(0)+" "+tipo);
						error = false;	
					}
				}
			}else{
								
					posicion = obtenerPosicion(lexema);
					if (posicion != -1){				
						System.out.println("TS: InsertarVariable: Si estaba en tabla Simbolos " +lexema+" , modificado el tipo "+tipo);
						tabla.get(posicion).tipo = tipo;
						error = false;
					}else{
						
						fila = new filaTS(lexema, tipo, tipoVisibilidad.GLOBAL, 0, 0, null, null, null);
						tabla.add(fila);
						System.out.println("TS: InsertarVariable: No estaba en tabla Simbolos: Crear filaTS: "+lexema+" "+tipo);
						error = false;				
					}
				
			}
		}else{	
			error = ((TablaFuncion) tablaActual).insertarParametro(lexema, tipo);
		}
		
		System.out.println("TS: InsertarVariable: error "+ error);

		return error;
	}
	
	
	
	
	public boolean insertarFuncionTS(String lexema){ 
		boolean error=true; int posicion;
		filaTS fila=null; 
		TablaFuncion tablaFuncion = null;
		
		System.out.println("TS: InsertarFuncionTS: "+lexema);
		posicion = obtenerPosicion(lexema);
		
		if (posicion!=-1){
			System.out.println("TS: InsertarFuncionTS: Si estaba en tabla Simbolos: Error "+lexema);
			error = true;

		}else{	
			tablaFuncion = new TablaFuncion(tabla.size());
			fila = new filaTS(lexema, tipo.FUNCION, tipoVisibilidad.GLOBAL, 0, 0, tipo.VAR_DESCONOCIDA, tablaFuncion, null);
			tabla.add(fila);
			tablaActual=tablaFuncion;
			System.out.println("TS: InsertarFuncionTS: No estaba en tabla: Crear filaTS: "+lexema);

			error = false;
		}
		
		System.out.println("TS: InsertarFuncionTS: error "+ error);
		
		return error;
	}
	
	
	
	
	public boolean insertarVector(String lexema, int dimension){ 
		boolean error=true; int posicion;
		filaTS fila=null; 
		TablaVector tablaVector = null;
		tipoVisibilidad visibilidad = null;
		
		System.out.println("TS: InsertarVector: "+lexema + " dimension " + dimension);
		
		if (tablaActual==tabla){
		
			posicion = obtenerPosicion(lexema);
			
			if (posicion!=-1){
				
				tabla.get(posicion).tipo = tipo.VECTOR;
				tablaVector = new TablaVector();
				tabla.get(posicion).tablaVector = tablaVector;
				tabla.get(posicion).dimension = dimension;
				
				for (int i=0; i<dimension;i++){
					tabla.get(posicion).tablaVector.insertarTVector(i, tipo.VAR_DESCONOCIDA);
				}
				
				
				System.out.println("TS: InsertarVector: Si estaba en tabla: Modifica a tipo vector "+lexema);
				error = false;
	
			}else{	
				tablaVector = new TablaVector();
				
				if (tablaActual==tabla)
					visibilidad = tipoVisibilidad.GLOBAL;
				else
					visibilidad = tipoVisibilidad.LOCAL;
				
	
				fila = new filaTS(lexema, tipo.VECTOR, visibilidad, dimension, 0, null, null, tablaVector);
				tabla.add(fila);
				System.out.println("TS: InsertarVector: No estaba en tabla: Crear filaTS: "+lexema);
	
				error = false;
			}
		}else{
			
			
			error = ((TablaFuncion) tablaActual).insertarVector(lexema, dimension);
			
		}
		
		System.out.println("TS: InsertarVector: error "+ error);

		return error;
	}
	
	
	
	public int obtenerPosicion(String lexema){
		int posicion=-1,i=0; 
		boolean encontrado=false;
		int indice;
		
		System.out.println("TS: ObtenerPosicion "+lexema);

		if (tablaActual==tabla){
			
			if (lexema.equals("ACTUAL")){
				
				if ((indice = pilaVariablesActuales.get(0).indexOf("|"))!=-1){
				
					lexema = ( pilaVariablesActuales.get(0).substring(0, indice) );
					System.out.println("TS: ObtenerPosicion "+lexema + " aunque es un elemento vector");
	
					
				}else
					lexema = pilaVariablesActuales.get(0);	
			}
				
				if (!((ArrayList<filaTS>) tablaActual).isEmpty()){
					Iterator<filaTS> iteradorFila = ((ArrayList<filaTS>) tablaActual).iterator();
					String cadena;
					
						while (iteradorFila.hasNext() && !encontrado){
							cadena = (iteradorFila.next()).lexema;
		
							if (lexema.equals(cadena)){
								posicion = i;
								encontrado=true;
							}
							i++;
						}
								
				}else{
					posicion = -1;
				}
		}else{
			posicion = ((TablaFuncion) tablaActual).obtenerPosicion(lexema);
		
			if (posicion==-1){
				
				if (!tabla.isEmpty()){
					Iterator<filaTS> iteradorFila = tabla.iterator();
					String cadena;
					
						while (iteradorFila.hasNext() && !encontrado){
							cadena = (iteradorFila.next()).lexema;
		
							if (lexema.equals(cadena)){
								//posicion = i;
								encontrado=true;
							}else
							i++;
						}
						
						
						if ( encontrado && tabla.get(i).tipo.equals(tipo.FUNCION) && tablaActual.equals(tabla.get(i).tablaFuncion) ){
							posicion = i;
						}else{
							posicion = -1;
						}
				
								
				}else{
					posicion = -1;
				}
				
				
			}
			
		}
		
		
		System.out.println("TS: ObtenerPosicion "+lexema+ " = " + posicion);
		return posicion;
	}
	
	
	public boolean insertarParametroTFuncion(String parametro, tipo tipo){
		boolean error = false;
		
		System.out.println("TS: InsertarParametroTFuncion "+ parametro + " tipo " + tipo);
	
			error = ((TablaFuncion) tablaActual).insertarParametro(parametro, tipo);
			tabla.get(((TablaFuncion) tablaActual).posicionTS).numeroParametros++;
			
		System.out.println("TS: InsertarParametroTFuncion: error "+ error);
	
		return error;
	}
	
	
	
	
	
	public boolean cambiarContexto(String nombreTabla){
		boolean error=false;
		
		System.out.println("TS: CambiarContexto: tablaActual es "+ nombreTabla);
		
		if (nombreTabla.equals("null"))
			tablaActual = this.tabla;
		else{
			int posicion = obtenerPosicion(nombreTabla);
			if (posicion==-1){
				error = true;
			}else{
				tablaActual = tabla.get(posicion).tablaFuncion;
			}
		}
		
		System.out.println("TS: CambiarContexto: error "+ error);

		return error;
	}
	
	
	
	public boolean cambiarContextoVariable(String lexema){
		
		System.out.println("TS: CambiarContextoVariable: variableActual es "+ lexema);
		
		if (tablaActual==tabla){
			if (lexema.equals("null"))
				pilaVariablesActuales.remove(0);
			else
				this.pilaVariablesActuales.add(0, lexema);
		}else{ 
			((TablaFuncion) tablaActual).cambiarContextoVariable(lexema);
		}
		
		System.out.println("TS: CambiarContextoVariable: pila de variables actuales: "+ pilaVariablesActuales);
		
		return false;
	}
	
	
	public tipo obtenerTipo(String lexema){
		tipo tipo = null;
		int posicion=0;
		int indice;
		
		System.out.println("TS: ObtenerTipo: lexema "+ lexema);
		
		if (tablaActual==tabla){
			
			if (lexema.equals("ACTUAL")){
				lexema = pilaVariablesActuales.get(0);
			}
			
			
				if ((indice = lexema.indexOf("|"))!=-1){
					
					posicion = obtenerPosicion ( lexema.substring(0, indice) );					
					
					if ( tabla.get(posicion).tablaVector.tablaVector.size() >= Character.getNumericValue(lexema.charAt(indice+1)+1) )
						tipo = tabla.get(posicion).tablaVector.obtenerTipo(  Character.getNumericValue(lexema.charAt(indice+1)) );
					else 
						System.out.println("TS: ObtenerTipo: "+pilaVariablesActuales.get(0)+" no se ha podido obtener tipo");
					
				}else{
					
					
					
					if ((indice = lexema.indexOf("/"))!=-1){
						
						posicion = obtenerPosicion ( lexema.substring(0, indice) );					
						
						if ( tabla.get(posicion).tablaFuncion.tablaFuncion.size() <= indice+1 )
							tipo = tabla.get(posicion).tablaFuncion.tablaFuncion.get(  Character.getNumericValue(lexema.charAt(indice+1)) ).tipo;
						else 
							System.out.println("TS: ObtenerTipo: "+pilaVariablesActuales.get(0)+" no se ha podido obtener tipo");
						
					
					
					}else{
						posicion = obtenerPosicion(lexema);
						if (posicion!=-1){
							tipo = tabla.get(posicion).tipo;
						}
					}
				}
			
		}else{
		
			tipo = ((TablaFuncion) tablaActual).obtenerTipo(lexema);	
			
			
			if (tipo == null){
				
				if ((indice = lexema.indexOf("|"))!=-1){
					
					posicion = obtenerPosicion ( lexema.substring(0, indice) );	
					
					if (tabla.get(posicion).tipo==tipo.FUNCION && tabla.get(posicion).tablaFuncion.equals(tablaActual) ){
					
						if ( tabla.get(posicion).tablaVector.tablaVector.size() <= indice+1 )
							tipo = tabla.get(posicion).tablaVector.obtenerTipo(  Character.getNumericValue(lexema.charAt(indice+1)) );
						else 
							System.out.println("TS: ObtenerTipo: "+pilaVariablesActuales.get(0)+" no se ha podido obtener tipo");
					}
						
				}else{
					
					
					
					if ((indice = lexema.indexOf("/"))!=-1){
						
						posicion = obtenerPosicion ( lexema.substring(0, indice) );		
						
						if (tabla.get(posicion).tipo==tipo.FUNCION && tabla.get(posicion).tablaFuncion.equals(tablaActual) ){

						
							if ( tabla.get(posicion).tablaFuncion.tablaFuncion.size() <= indice+1 )
								tipo = tabla.get(posicion).tablaFuncion.tablaFuncion.get(  Character.getNumericValue(lexema.charAt(indice+1)) ).tipo;
							else 
								System.out.println("TS: ObtenerTipo: "+pilaVariablesActuales.get(0)+" no se ha podido obtener tipo");
							
						}
					
					}else{
						posicion = obtenerPosicion(lexema);
						if (posicion!=-1 && tabla.get(posicion).tipo==tipo.FUNCION && tabla.get(posicion).tablaFuncion.equals(tablaActual)){
							tipo = tabla.get(posicion).tipo;
						}
					}
				}
				
				
			}
			
			
			
		}
		
		System.out.println("TS: ObtenerTipo: lexema "+ lexema + " tipo " + tipo);

		return tipo;
	}
	
	
	
	public boolean actualizarTipo(String lexema, tipo tipo){
		boolean error = true; 
		int posicion;
		int indice;
		
		System.out.println("TS: ActualizarTipo: lexema "+ lexema + " tipo " + tipo);
		
		if (tablaActual==tabla){
			
			if (lexema.equals("ACTUAL")){
				
				if ((indice = pilaVariablesActuales.get(0).indexOf("|"))!=-1){
						
					posicion = obtenerPosicion ( pilaVariablesActuales.get(0).substring(0, indice) );					
					
					if ( pilaVariablesActuales.get(0).length() > indice+1 )
						error = tabla.get(posicion).tablaVector.actualizarTVector( Character.getNumericValue(pilaVariablesActuales.get(0).charAt(indice+1) )  , tipo);
					else 
						System.out.println("TS: ActualizarTipo: "+pilaVariablesActuales.get(0)+" no se ha podido actualizar");


				}else{
					
					posicion = obtenerPosicion(pilaVariablesActuales.get(0));
					
					if (posicion == -1){
						error = insertarVariable(lexema, tipo);
					}else{
						tabla.get(posicion).tipo = tipo;
						error = false;
					}
					
				}
			}else{
				
				posicion = obtenerPosicion(lexema);
				
				if (posicion == -1){
					error = insertarVariable(lexema, tipo);
				}else{
					tabla.get(posicion).tipo = tipo;
					error = false;
				}
				
				
			}
			
						
			
		}else{
			
			error = ((TablaFuncion) tablaActual).actualizarTipo(lexema, tipo);		
		
			
			if (error){
				posicion = obtenerPosicion(lexema);
				
				if (posicion == -1){
					error = ((TablaFuncion) tablaActual).insertarParametro(lexema, tipo);		
				}else{
					tabla.get(posicion).tipo = tipo;
					error = false;
				}
			}
		}
		
		System.out.println("TS: ActualizarTipo: error "+ error);

		return error;
	}
	
	


	public tipo obtenerTipoDevuelto(String lexema){
		tipo tipo = null;
		int posicion=0;
		boolean encontrado = false;
		
		System.out.println("TS: ObtenerTipoDevuelto: lexema "+ lexema);
					
			while (posicion<=tabla.size()-1 && !encontrado){
				if (tabla.get(posicion).lexema.equals(lexema) && tabla.get(posicion).tipo == tipo.FUNCION)
					encontrado = true;
				else
					posicion++;
			}
			
			if (encontrado){
	
				tipo = tabla.get(posicion).tipoDevuelto;
			}
		
		System.out.println("TS: ObtenerTipoDevuelto: lexema "+ lexema + " tipo " + tipo);

		return tipo;
	}
	
	
	public int obtenerDimension(String lexema){
		int dimension = 0;
		int posicion=0;
				
		System.out.println("TS: ObtenerDimension: lexema "+ lexema);
		
		if (tablaActual==tabla){

			if (lexema.equals("ACTUAL")){
				lexema = pilaVariablesActuales.get(0);
			}
				
				posicion = obtenerPosicion(lexema);
				
				if (posicion!=-1 && (obtenerTipo(lexema) == tipo.VECTOR) ){
		
					dimension = tabla.get(posicion).dimension;
				}
			
			System.out.println("TS: ObtenerDimension: lexema "+ lexema + " dimension " + dimension);
			
		}else
			dimension = ((TablaFuncion) tablaActual).obtenerDimension(lexema);

		return dimension;
	}
	
	
	
	
	

	public boolean copiarVector(String v1, String v2) {
		
		System.out.println("TS: CopiarVector: v1 = "+ v1 + ", v2 = " + v2);

		if (v1.equals("ACTUAL")){
			v1 = pilaVariablesActuales.get(0);
		}
		if (v2.equals("ACTUAL")){
			v2 = pilaVariablesActuales.get(0);
		}
		int posicion1 = obtenerPosicion(v1);
		int posicion2 = obtenerPosicion(v2);
		
		int i=0;
		for (filaTVector fila : tabla.get(posicion1).tablaVector.tablaVector){
			tabla.get(posicion1).tablaVector.actualizarTVector(i, 
																tabla.get(posicion2).tablaVector.tablaVector.get(i).tipo, 
																tabla.get(posicion2).tablaVector.tablaVector.get(i).desplazamiento);
		i++;
		}	
		
		return false;
	}

	public boolean eliminarElemento(String string) {
		boolean error = true; int posicion;
		System.out.println("TS: EliminarElemento: lexema = "+ string);
		
		if (tablaActual == tabla){
			if ((posicion = obtenerPosicion(string))!=-1 ){
				tabla.remove(posicion);
				error = false;
			}
			else
				error = true;
		}else
			error = ((TablaFuncion) tablaActual).eliminarElementoTFuncion(string);
		
		System.out.println("TS: EliminarElemento: error = "+ error);
		return error;
			
	}
	
	
	
	
	public String obtenerVariableActual (int i){
		
		if (tablaActual==tabla)
			return pilaVariablesActuales.get(i);
		else
			return ((TablaFuncion) tablaActual).obtenerVariableActual(i);
	}

	
	
	public boolean actualizarTipoDevuelto(tipo tipoReturn) {
		boolean error = false;
		
		if (tablaActual==tabla)
			error = true;
		else{
			// dentro de funcion
			int i=0;
			while (tabla.get(i).tablaFuncion==null || !tabla.get(i).tablaFuncion.equals(tablaActual)){
				i++;
			}
		
			
			if(tabla.get(i).tipoDevuelto != tipoReturn){
				
				if (tabla.get(i).tipoDevuelto == null || tabla.get(i).tipoDevuelto.equals(tipo.VAR_DESCONOCIDA) ){
					tabla.get(i).tipoDevuelto = tipoReturn;
					System.out.println(tabla.get(i).tipoDevuelto);
				}else
					error = true;
			}
		}
		System.out.println("TS: ActualizarTipoDevuelto: error = " + error);
		return error;
	}


	public boolean esFuncion (String lexema){
		int i = 0;boolean resultado = false;
		
		while (i+1<=tabla.size() && !resultado){
			if (tabla.get(i).lexema.equals(lexema) && tabla.get(i).tipo == tipo.FUNCION)
				resultado = true;
			i++;
		}
		
		return resultado;
	}

	
	public int obtenerNumeroParametros(String lexema) {
		int nparametros = -1;
		int posicion=0;
				
		System.out.println("TS: ObtenerNumeroParametros: lexema "+ lexema);
		
		if (tablaActual==tabla){

			if (lexema.equals("ACTUAL")){
				lexema = pilaVariablesActuales.get(0);
			}
				
			posicion = obtenerPosicion(lexema);
			
			if (posicion!=-1 && (obtenerTipo(lexema) == tipo.FUNCION) ){
				nparametros = tabla.get(posicion).numeroParametros;
			}
			
			System.out.println("TS: ObtenerNumeroParametros: lexema "+ lexema + " nparametros " + nparametros);
			
		}else{
			
			posicion = obtenerPosicion(lexema);
			
			if (posicion!=-1 && (tabla.get(posicion).tipo == tipo.FUNCION) && tablaActual.equals(tabla.get(posicion).tablaFuncion) ){
				nparametros = tabla.get(posicion).numeroParametros;
			}
			
			
		}
		
		System.out.println("TS: ObtenerNumeroParametros: lexema "+ lexema + " n parametro "+ nparametros);

		return nparametros;
	}
	
	
	
	public int obtenerDesplazamiento(String lexema){
		int desplazamiento;
		int posicion = obtenerPosicion(lexema);
		
		if (tablaActual == tabla){
			
			desplazamiento = tabla.get(posicion).desplazamiento;
			
			
		}else
			return ((TablaFuncion) tablaActual).obtenerDesplazamiento(lexema);
		
		
		return desplazamiento;
		
	}
	
	
	
	public void actualizarDesplazamiento (){
		int contador=0;
		
		System.out.println("TS: actualizarDesplazamiento");
		
		for (filaTS elemento : tabla){
			System.out.println(elemento.lexema + " " + elemento.tipo);
			switch (elemento.tipo){
			
			case ENTERO: case BOOL: case STRING: case VAR_DESCONOCIDA: 
				elemento.desplazamiento = contador;
				contador = contador + 8;
				break;
			
			case FUNCION: 
				elemento.desplazamiento = contador;
				contador = elemento.tablaFuncion.actualizarDesplazamiento(contador);
				break;
			case VECTOR: 
				elemento.desplazamiento = contador;
				contador = elemento.tablaVector.actualizarDesplazamiento(contador);
				break;
			default: elemento.desplazamiento = contador;
			contador = contador + 8;
			break;
			}
		}
	}
	
	
	
	
	public int obtenerTotalDesplazamiento(){
		int total=-1;
		
		
		switch(tabla.get( tabla.size()-1 ).tipo){
		
		case FUNCION:
				total = tabla.get( tabla.size()-1 ).tablaFuncion.obtenerTotalDesplazamiento();
			break;
			
		case VECTOR:
				total = tabla.get( tabla.size()-1 ).tablaVector.obtenerTotalDesplazamiento();
			break;
			
			default: total = tabla.get( tabla.size()-1 ).desplazamiento;

		}
		return total;
	}
	
	
	
	
	
}
